package consul

import (
	"encoding/json"
	"fmt"
	"github.com/google/wire"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
	"go.uber.org/zap"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"
	time2 "time"
)

type Service struct {
	Name string   `json:"name"`
	Tags []string `json:"Tags"`
}

type Consul struct {
	url        string `json:"url" yaml:"config"`
	logger     *zap.Logger
	m          map[string]string
	syn        sync.Mutex
	httpclient *http.Client
}

func New(logger *zap.Logger, httpclient *http.Client) *Consul {
	consul := &Consul{
		url:        viper.GetString("consul.url"),
		logger:     logger,
		httpclient: httpclient,
		m:          make(map[string]string),
	}
	consul.Run()
	go func() {
		time := time2.Tick(1 * time2.Hour)
		select {
		case <-time:
			consul.Run()
		}
	}()
	return consul
}

func (c *Consul) GetZeusMap() map[string]string {
	m := make(map[string]string)
	c.syn.Lock()
	defer c.syn.Unlock()
	for k, v := range c.m {
		m[k] = v
	}
	return m
}

func (c *Consul) GetZeus(dc string) (map[string]string, error) {
	m := make(map[string]string)
	request, err := http.NewRequest("GET", "http://"+c.url+"/v1/internal/ui/services?dc="+dc, nil)
	if err != nil {
		c.logger.Error("call h.httpClient.Get() error :" + c.url)
		return nil, err
	}

	resp, err := c.httpclient.Do(request)
	if err != nil {
		c.logger.Error("call h.httpClient.Get() error :" + c.url)
		return nil, err
	}
	defer resp.Body.Close()
	if err != nil {
		c.logger.Error("read response body error: " + err.Error())
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		c.logger.Error("read response body error: " + err.Error())
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		err := errors.New(fmt.Sprintf("response status code is %v", resp.StatusCode))
		c.logger.Error(err.Error())
		return nil, err
	}
	services := make([]*Service, 0)
	if err := json.Unmarshal(body, &services); err != nil {
		c.logger.Error(err.Error())
		return nil, err
	}
	for _, s := range services {
		if len(s.Tags) == 0 {
			continue
		}
		for _, t := range s.Tags {
			if strings.HasPrefix(t, "APP:") {
				app := strings.ReplaceAll(t, "APP:", "")
				m[app] = s.Name
			}
		}
	}
	return m, nil
}

func (c *Consul) Run() {
	c.logger.Info("sync host resources cron start")
	dc := viper.GetString("consul.dc")
	dcs := strings.Split(dc, ",")
	c.syn.Lock()
	defer c.syn.Unlock()
	for _, d := range dcs {
		services, err := c.GetZeus(d)
		if err != nil {
			c.logger.Error("get consul error", zap.Error(err))
			continue
		}
		for k, v := range services {
			c.m[k] = v
		}
	}
	c.logger.Info("sync host resources cron end")
}

var ProviderSet = wire.NewSet(New)
